//================================================头文件包含======================================================//
#include <string.h>
#include "userboot.h"

#include "bsp_init.h"
#include "log.h"


/* 必看说明 : 由于 UserBoot 属于一个功能模块, 需要结合 APP 使用, 且 UserBoot 并不是所有人都需要用, 故此处没有帮用户设置成 <开箱即用> 的方式.
 * 针对本工程内所有目标(除 UserBoot 自身) => 要使用 UserBoot , 
 * 必须在 Keil 下修改 每个 APP 固件 的 下载地址 与 大小, 将固件 下载起始地址 设为 [0x10000], 大小 设为 [0x70000].
 * 
 * 修改方法须根据 在 Keil 工程的 Option -> Linker 的 [User Memory Layout from Target Dialog] 选项是否被<勾选> 分为如下情况:
 * 一、[未勾选 X ] : 在 Keil 工程的 Option -> Linker 的 Scatter File 项点击 Edit... , 修改 [LR_IROM1] 与 [ER_IROM1] 的 <Start> <Size> 值
 * 二、[已勾选 √ ] : 在 Keil 工程的 Option -> Target -> Read Only Memory Areas [on-chip] 中设置 <已被勾选的> Start 与 Size 值
 * 如无特殊情况, 本例程已将所有目标(除 UserBoot 自身)设为上述第 一 种情形, 两种情形互斥, 一方有效则另一方无效.
 * 
 * 而对于 UserBoot 目标, 笔者已将各项值设定好/无须用户修改, 此外, 上述所有值均依赖于本文件的 <UserBoot_Size = 0x10000> 计算得出.
 */



//由于本例程适配各种分辨率的屏, 加之考虑到更新过程不会经常触发, 本着在 UB 中尽量精简 外设/功能 的设计原则, 故目前的 UserBoot 未实现屏显.
//更新过程中 可以增加不同颜色 LED 灯 快/慢 闪烁、蜂鸣器发声 等用户提示.


/* UserBoot 编写说明 : (OTA须借助外部无线模块) Device Firmware Update
 *
 * 加载方式 : 待更新的固件以文件形式存储于用户外部存储介质中, 待用户插入外部存储介质,
 * 将待更新的固件拷贝至 SPI-Flash 本地离线存储, 便于当用户选择合适的时机再(重启)执行更新动作 拷贝至 Chip-Flash 执行
 * 如此使得 固件下载拷贝 与 固件更新 的动作得到分隔, 当用户执行 固件更新 时, 如检测到 Chip-Flash 中已有固件, 
 * 会将 旧固件 备份至 SPI-Flash 本地离线存储, 用于系统宕机时的 固件恢复.
 *
 * 存储方式 : 双分区[备份区放在 SPI-Flash ] <若备份区放在 Chip-Flash 对 Chip-Flash 大小要求较高, 需要压缩 APP>,
 * 后台式下载新固件至 SPI-Flash , 在更新至 chip-Flash 时作读出 旧固件 备份至 SPI-Flash, 
 * 若固件更新成功, 则保留在 SPI-Flash 本地离线存储 的待更新固件 会删除掉, 避免触发重复更新动作.
 *
 * 加密方式 : 固件(*.bin)头部加入固件信息头(带 CRC 校验),
 * 至于对每一段都加入 CRC 个人认为没有太大的必要, 因为在数据传输层已经可以验证数据有效性了,
 * 利用每一颗芯片内的 UID 作 <密钥> 将存储在 SPI-Flash 本地的离线固件进行加密,
 * 除本机外其他机器无法使用, 但不利于 SPI-Flash 母片批量烧录方法
 *
 * (SWM341 没有硬件加密模块, 须软件实现加密算法: 对称 - AES, 非对称 - RSA/ECC, 哈希 - MD5<不可还原>)
 *
 * 须提示用户, 在更新时出现1~2次重启是正常现象, 例如 Windows 更新.
 */

//================================================本地类型声明=======================================================//
//以下 结构体总大小须保持 4 Word 对齐(因 FLASH_Write() 最少写入 4 个字)
typedef struct
{
	uint32_t version; // 固件 版本(版本均可约定以程序日期为例"2022.10.16 => 0x20221016")
	uint32_t size;	  // 固件 大小(不包括 附加信息 )
	uint32_t crc;	  // 固件 CRC 校验和(使用 固件版本 + 固件大小 的结果作 CRC 数据源)
	uint32_t reserve;
} fw_info_t; // 固件信息

typedef struct
{
	uint32_t boot;		   // UserBoot 引导启动标志
	uint32_t boot_version; // UserBoot 版本(版本均可约定以程序日期为例"2022.10.16 => 0x20221016")
	uint32_t reserve[2];   
	fw_info_t app;		   // 固件信息
} dev_info_t;			   // 设备信息

//================================================本地常量定义=======================================================//
#define Flash_Alignment (4096)												   // Flash 对齐大小( 4K 对齐 )
static const uint32_t Chip_Flash_Max_Size = 0x80000;						   // SWM341 系列 Flash 容量上限 512K Bytes
static const uint32_t UserBoot_Addr = 0x00000;								   // UserBoot 加载地址
static const uint32_t UserBoot_Size = 0x10000;								   // UserBoot 预设大小 <4K 对齐> (可预留多一些, 以便后续有更新 UserBoot 的需求, 暂未预留接口实现)
static const uint32_t UserBoot_Version = 0x20221016;						   // UserBoot 程序版本
static const uint32_t Firmware_Addr = UserBoot_Addr + UserBoot_Size;		   // APP 加载地址
static const uint32_t Firmware_Max_Size = Chip_Flash_Max_Size - Firmware_Addr; // APP 大小上限

// 将设备信息存储于 ROM 中(可以是 Chip-Flash 或 SPI-Flash )
// 以 4K Bytes 地址对齐存储于 UserBoot 末尾 <-> APP 运行地址前
static const dev_info_t *const Device_Info = (const dev_info_t *const)(Firmware_Addr - Flash_Alignment);
//注意 : 该定向指针只读, 不允许写入, 写入须调用 set_device_info() 函数


//当第一次上电没有固件时, 默认去搜寻的外部磁盘类型
static const disk_t Move_Disk = USB;

//================================================全局常量定义=======================================================//
// 存放至本地 (SPI-Flash) 的固件文件名
const char *const Firmware_Name_Update = "Firmware_SWM34S_Admin.bin";
const char *const Firmware_Name_Backup = "Firmware_SWM34S_Backups.bin";
// 本地磁盘类型
const disk_t Firmware_Disk = SPI;

//================================================全局变量定义=======================================================//

//================================================本地变量定义=======================================================//
//读写缓冲区
static uint8_t Read_Buff[Flash_Alignment] __attribute__((aligned(4)));
static uint8_t Write_Buff[Flash_Alignment] __attribute__((aligned(4)));

#ifdef UB_SECURE_AES_ENABLE
// AES 
//#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/aes.h"
//安全加密 AES
static mbedtls_aes_context AES;
// AES 下的初始化向量 ( Intialization Vector ) <与明文分组对应的密文分组>
static uint8_t IV[16];
//向量默认初值
static uint8_t IV_Init[16] = {
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
#endif

//================================================本地函数声明=======================================================//
static void reset_mcu(void);
static void jump_to_app(const uint32_t addr);
static uint32_t calculate_crc(fw_info_t *data);
static uint8_t set_device_info(const dev_info_t *ptr);
static uint8_t firmware_backup(const char *path, data_code_cb data_enc);
static uint8_t firmware_update(const char *path, data_code_cb data_dec);

//=================================================函数定义========================================================//
/**
 * @brief	UserBoot 功能
 * @param	/
 * @retval	/
 * @note	本函数会一直阻塞在被调用处, 之后的语句都不会得到执行
 */
void userboot(void)
{
	/* 已在 startup_SWM341.s 的 Reset_Handler 中调用 
	SystemInit();
	sfc_init();   //初始化总线上外挂的SPI-Flash, 将其映射至MCU内部(可选外设)
	//sdram_init(); //初始化总线上外挂的SDRAM<芯片内置>, 将其映射至MCU内部(可选外设)
	*/
	
	//TODO------------特权超级管理员操作 :  
	//<这个时间是为了让用户有充足时间将 IO 接高> 3s 以内识别 A8/ISP拉高则, 优先从外部存储中(USB)识别固件并更新至Chip-Flash运行区, 类似于工厂的出厂模式
	
	if (SYS->RSTSR & SYS_RSTSR_POR_Msk)//1 出现过POR复位，写1清零
	{
		SYS->RSTSR |= (1 << SYS_RSTSR_POR_Pos);
	}
	if (SYS->RSTSR & SYS_RSTSR_WDT_Msk)//1 出现过WDT复位，写1清零
	{
		SYS->RSTSR |= (1 << SYS_RSTSR_WDT_Pos);
	}
	
	//检测启动引导标志位
	if (Device_Info->boot == GO_APP)
	{
		/* 跳转到APP前需要将 UserBoot 使用的外设关掉（复位）*/
		__disable_irq();
		
		SYS->PRSTEN = 0x55;
		SYS->PRSTR0 = 0xFFFFFFFF & (~SYS_PRSTR0_ANAC_Msk);//ANAC模拟控制单元
		SYS->PRSTR1 = 0xFFFFFFFF;
		for(int i = 0; i < CyclesPerUs; i++) __NOP();
		SYS->PRSTR0 = 0;
		SYS->PRSTR1 = 0;
		SYS->PRSTEN = 0;
		
		NVIC->ICER[0] = 0xFFFFFFFF;
		NVIC->ICER[1] = 0xFFFFFFFF;
		NVIC->ICER[2] = 0xFFFFFFFF;

		jump_to_app(Firmware_Addr);
	}
	else 
	{
		uint8_t res = 0;

		uart_debug_init();//仅用作调试日志打印
		
		crc_init();
	
		//芯片可以提供唯一 96 bit ID 号用于加密使用
		uint32_t chip_id[128 / 32];
		chip_id[0] = SYS->CHIPID[0];
		chip_id[1] = SYS->CHIPID[1];
		chip_id[2] = SYS->CHIPID[2];
		chip_id[3] = SYS->CHIPID[0];//不足的按重复填充
		printf("The [Chip_ID] = 0x[%x][%x][%x]\r\n", chip_id[0], chip_id[1], chip_id[2]);
		
		#ifdef UB_SECURE_AES_ENABLE
		//初始化 AES 算法, 并设置密钥
		mbedtls_aes_init(&AES);
		mbedtls_aes_setkey_enc(&AES, (const uint8_t *)chip_id, 128);
		mbedtls_aes_setkey_dec(&AES, (const uint8_t *)chip_id, 128);
		// mbedtls_aes_free(&AES);
		
		/* 使用硬件 PRNG 随机数 填充 IV 初值
		//设置密文分组的默认初值
		SYS->HRCCR |= (1 << SYS_HRCCR_ON_Pos);
		SYS->LRCCR |= (1 << SYS_LRCCR_ON_Pos);
		SYS->PRNGCR = (0 << SYS_PRNGCR_CLR_Pos) |
					  (3 << SYS_PRNGCR_MODE_Pos);
		for (uint32_t i = 0; i < sizeof(IV) / sizeof(IV[0]); ++i)
		{
			while ((SYS->PRNGCR & SYS_PRNGCR_RDY_Msk) == 0)
				__NOP();
			IV_Init[i] = SYS->PRNGDH + SYS->PRNGDL;
			// printf("PRNGDL: %08X, PRNGDH: %08X\n", SYS->PRNGDL, SYS->PRNGDH);

			for (i = 0; i < SystemCoreClock / 8; i++)
				__NOP();
		}
		*/
		memcpy(IV, IV_Init, sizeof(IV)); //重置密文分组

		#endif
		
		char firmware_path_update[FF_MAX_LFN]; //更新固件的绝对路径
		sprintf(firmware_path_update, "%s%s", VolumeStr[Firmware_Disk], Firmware_Name_Update);

		char firmware_path_backup[FF_MAX_LFN]; //备份固件的绝对路径
		sprintf(firmware_path_backup, "%s%s", VolumeStr[Firmware_Disk], Firmware_Name_Backup);

		char firmware_path_move[FF_MAX_LFN]; //外部固件的绝对路径
		sprintf(firmware_path_move, "%s%s", VolumeStr[Move_Disk], Firmware_Name_Update);	

		res = fs_mount(Firmware_Disk);
		if (res != 0) //本地存储挂载文件系统
		{
			printf("The [fs_mount] <Firmware_Disk[%d]> result is [%d]!\r\n", Firmware_Disk, res);
			//while (1) __NOP();//不允许出现本地存储无法挂载的情况, 这会导致 UserBoot 功能无法执行
			
			//若出现此种情况, 此处应执行一些特定严重警告的语句以提示用户
		}
		else 
		{
			switch (Device_Info->boot)
			{
			/* 
			case (GO_APP): //跳转至 app 运行
				jump_to_app(Firmware_Addr);
				break;
			*/
			case (EXEC_UPDATE): //检测到有可更新的固件, 进行更新
				res = firmware_backup(firmware_path_backup, data_encode);
				if (res == 0)//进行更新前将 旧固件 备份
				{
					res = firmware_update(firmware_path_update, data_decode);
					if (res == 0) //更新完成, 可以跳转
					{
						jump_to_userboot(GO_APP);
					}
				}
				//上述工作流程出现错误
				printf("The [EXEC_UPDATE] backup[%s] update[%s], result is [%d]!\r\n", firmware_path_backup, firmware_path_update, res);
				break;
				
			case (BACKUP_RESTORE): //出现错误变砖后, 从备份区恢复固件
				res = firmware_update(firmware_path_backup, data_decode);
				if (res == 0) //写入旧固件完成, 可以跳转恢复
				{
					jump_to_userboot(GO_APP);
				}
				//上述工作流程出现错误
				printf("The [BACKUP_RESTORE] backup[%s], result is [%d]!\r\n", firmware_path_backup, res);
				break;

			default: //这是 UserBoot 第一次启动, 没有经过 APP 更新函数 故必然没有固件头, 此时应检测是否存在通过 Keil 下载的原生固件

				/*	通过判断栈定地址值是否在 0x2000 0000 - 0x2001 0000 之间 ( SWM341 RAM Max : 0x1 0000 <64KB> ) 来判断应用程序是否已经下载到指定地址.
				 *	因为中断向量表里第一个放的就是栈顶地址的值, 如果该值不在 RAM 区间地址内, 即使这是固件也是不合法的.
				 */
				if ( ( ( *(volatile uint32_t *)(Firmware_Addr) ) & 0x2FFE0000 ) == 0x20000000 || 
					( ( *(volatile uint32_t *)(Firmware_Addr) ) & 0x0001FFFF ) < 0x00010000 )
				{
					//原生固件已经存在, 可以直接运行 app (此处默认原生固件可正常执行无误, 该项由用户保证, 因为这是用户通过 Keil 下载的, 理应如此)
					printf("APP Already Exist!\r\n");
					
					dev_info_t dev_info;
					dev_info.boot = GO_APP;
					//配置写入默认初始化固件信息
					dev_info.boot_version = UserBoot_Version;
					dev_info.reserve[0] = 0;
					dev_info.reserve[1] = 0;
					// APP 默认版本跟随 UserBoot 版本, 因通过keil下载无法获取app大小, 故默认大小为除 UserBoot 占用之外的全部剩余 Chip-Flash 大小
					dev_info.app.version = UserBoot_Version;
					dev_info.app.size = Firmware_Max_Size;
					dev_info.app.crc = calculate_crc(&(dev_info.app));
					dev_info.app.reserve = 0;
					//写入设备配置信息
					res = set_device_info(&dev_info);
					if (res != 0)
					{
						printf("The [set_device_info] result is [%d]!\r\n", res);
					}
				}
				else
				{
					//没有原生固件, 可能是用户下载 userboot 时遗漏忘了 app , 
					//分别去 [ 外部存储(SD) 、本地存储(SPI-Flash) ] 寻找是否有可用固件, (外部存储权重更高)
					//若搜寻无果-没有可用固件, 需要提示用户升级/告警, 并停留在 UserBoot 中不断复位
					uint32_t i = 0;
					do {
						res = fs_mount(Move_Disk);
						for (uint32_t j = 0; j < CyclesPerUs; ++j) __NOP();
						++i;
					} while (i < CyclesPerUs * 1000 && res != 0);//检测一段时间
					
					if (res == 0) //外部存储挂载文件系统
					{
						res = firmware_download(firmware_path_move, firmware_path_update, data_encode);
						if (res == 0) //外部存储中有 可用更新固件, 成功下载至本地
						{
							res = firmware_update(firmware_path_update, data_decode);
							if (res == 0) //更新完成, 可以跳转
							{
								//由于是第一次下载的固件, 故将 新固件 备份
								res = firmware_backup(firmware_path_backup, data_encode);
								printf("The [firmware_backup] backup[%s], result is [%d]!\r\n", firmware_path_backup, res);
								
								//fs_unmount(Move_Disk);//外部存储卸载文件系统
								
								jump_to_userboot(GO_APP);
							}
						}
						fs_unmount(Move_Disk);//外部存储卸载文件系统
					}
					else //未插入外部存储
					{
						printf("The [fs_mount] <Firmware_Disk[%d]> result is [%d]!\r\n", Move_Disk, res);
						
						res = firmware_update(firmware_path_update, data_decode);
						if (res == 0) //更新完成, 可以跳转
						{
							//由于是第一次下载的固件, 故将 新固件 备份
							res = firmware_backup(firmware_path_backup, data_encode);
							printf("The [firmware_backup] backup[%s], result is [%d]!\r\n", firmware_path_backup, res);
							
							jump_to_userboot(GO_APP);
						}
						else 
						{
							res = firmware_update(firmware_path_backup, data_decode);
							if (res == 0) //更新完成, 可以跳转
							{
								printf("The [firmware_update] backup[%s], result is [%d]!\r\n", firmware_path_backup, res);
								jump_to_userboot(GO_APP);
							}
						}
					}
				}
				break;
			}
			fs_unmount(Firmware_Disk);//本地存储卸载文件系统
		}
	}
	reset_mcu();

	for (;;)
	{
		// Add Your Code
	}
	// No Exec Here <return>
}

/**
 * @brief	跳转至 UserBoot 执行对应动作
 * @param	boot_flag : UserBoot 引导启动标志(详见对应枚举)
 * @retval	/
 * @note	/
 */
void jump_to_userboot(uint32_t boot_flag)
{
	uint8_t res = 0;
	dev_info_t dev_info;
	dev_info.app = Device_Info->app;
	dev_info.boot_version = Device_Info->boot_version;
	dev_info.reserve[0] = Device_Info->reserve[0];
	dev_info.reserve[1] = Device_Info->reserve[1];
	
	switch (boot_flag)
	{
		case (GO_APP) 			: //continue
		case (EXEC_UPDATE) 		: //continue
		case (BACKUP_RESTORE) 	: //continue
			dev_info.boot = boot_flag;
		break;
		
		default : //传入其他的错误状态数据, 不生效
			dev_info.boot = Device_Info->boot;
			printf("[jump_to_userboot] is invalid! [boot_flag] = 0x[%x], [dev_info.boot] = 0x[%x]\r\n", boot_flag, dev_info.boot);
		break;
	}
	//写入设备配置信息
	res = set_device_info(&dev_info);
	if (res != 0)
	{
		printf("The [set_device_info] result is [%d]!\r\n", res);
	}
	reset_mcu();
	for (;;)
	{
		// Add Your Code
	}
}

/**
 * @brief	固件下载 (外部存储 => 本地存储)
 * @param	src_path	- 外部固件绝对路径
 *			dst_path	- 本地固件绝对路径
 * @param	data_enc 	- 数据加密回调
 * @retval	0-success	非0-error code
 * @note	须保证对应磁盘的 FAT 文件系统处于挂载状态
 */
uint8_t firmware_download(const char *src_path, const char *dst_path, data_code_cb data_enc)
{
	uint8_t res = 0;					  //返回值
	FIL src_file, dst_file;				  //文件指针
	uint32_t read_len = 0, write_len = 0; //读/写文件字节数
	uint32_t firmware_size = 0;			  //固件大小 / Bytes

	//	FA_CREATE_ALWAYS : 创建一个新文件，如果文件已存在，则它将被截断并覆盖
	//	FA_OPEN_ALWAYS	 : 如果文件存在，则打开；否则，创建一个新文件
	//	FA_OPEN_EXISTING : 打开一个文件。如果文件不存在，该函数将失败
	res = f_open(&src_file, src_path, FA_READ | FA_OPEN_EXISTING);
	if (FR_OK != res)
	{
		printf("[%s] file [f_open] = [%d]\r\n", src_path, res);
		return res;
	}
	res = f_open(&dst_file, dst_path, FA_WRITE | FA_OPEN_ALWAYS);
	if (FR_OK != res)
	{
		f_close(&src_file);
		printf("[%s] file [f_open] = [%d]\r\n", dst_path, res);
		return res;
	}
	//===============================================校验固件===============================================//
	//文件光标定位
	f_lseek(&src_file, 0);
	//获取固件大小
	firmware_size = f_size(&src_file);
	//获取固件信息
	fw_info_t firmware_info;
	res = f_read(&src_file, &firmware_info, sizeof(fw_info_t), &read_len);
	if (res != FR_OK || read_len < sizeof(fw_info_t))
	{
		if (read_len < sizeof(fw_info_t))
			res = 1;

		printf("The Firmware don't get info, result = [%d]!\r\n", res);
		goto firmware_download_over;
	}
	firmware_size -= read_len; //得出实际的固件大小
	printf("The [firmware_download] Version = 0x[%x], Size = 0x[%x], CRC = 0x[%x]\r\n", firmware_info.version, firmware_info.size, firmware_info.crc);

	//判断固件是否有效 / 固件信息是否准确
	if (firmware_info.size > Firmware_Max_Size ||
		firmware_info.size != firmware_size ||
		calculate_crc(&firmware_info) != firmware_info.crc)
	{
		res = 2;
		printf("The Firmware is invalid! calculate_crc = 0x[%x]\r\n", calculate_crc(&firmware_info));
		goto firmware_download_over;
	}
	// f_lseek(&src_file, 0);//不加密 固件信息头 (直接跳过), 对 备份/更新 同理
	//===============================================校验通过===============================================//
	#ifdef UB_SECURE_AES_ENABLE
	memcpy(IV, IV_Init, sizeof(IV)); //重置密文分组
	#endif
	//===============================================下载固件===============================================//
	f_lseek(&dst_file, 0);
	res = f_write(&dst_file, &firmware_info, read_len, &write_len);
	if (write_len < read_len) //写入不完整,大概率是目录已满
	{
		res = FR_DENIED;
		printf("[%s] file [read_len] = [%d], [write_len] = [%d], result = [%s]\r\n", dst_path, read_len, write_len, Debug_FR_Table[res]);
	}
	for (uint32_t offset = 0; FR_OK == res && offset < firmware_size; offset += write_len) //文件拷贝开始
	{
		res = f_read(&src_file, Read_Buff, sizeof(Read_Buff), &read_len);
		if (FR_OK != res || read_len == 0) //读取文件失败或者已读到文件结尾
		{
			// printf("[%s] last read_len = [%d] \r\n possible reason: [%d] file read over!\r\n", src_path, read_len, res);
			break;
		}

		//最后不足 16 Bytes 的部分进行 zero padding (因为 Flash_write 单次操作 Unit 是四个字, 无论用不用加密算法都要对齐) 
		for (; (read_len & 0xF) != 0; ++read_len)
			Read_Buff[read_len] = 0;
		
		if (data_enc != NULL) //加密
		{
			if (data_enc(Read_Buff, Write_Buff, read_len) != 0)
			{
				res = 3;
				printf("The Firmware [data_enc] result = [%d]!\r\n", res);
				break;
			}
		}
		else 
		{
			memcpy(Write_Buff, Read_Buff, read_len);
		}
		
		res = f_write(&dst_file, Write_Buff, read_len, &write_len);
		if (write_len < read_len) //最后一次写入不完整,大概率是目录已满
		{
			res = FR_DENIED;
			printf("[%s] file last [read_len] = [%d], [write_len] = [%d], result = [%s]\r\n", dst_path, read_len, write_len, Debug_FR_Table[res]);
			break;
		}

		printf("=============== [Firmware] DownLoad [%d]/[%d] Bytes ===============\r\n", offset + write_len, firmware_size);
	}
	//===============================================下载结束===============================================//
firmware_download_over:
	f_close(&src_file);
	f_close(&dst_file);
	if (0 != res) //出现错误则删除目标文件, 必须先关闭文件, 否则 f_unlink 将被拒绝
	{
		/*res = */ f_unlink(dst_path);
	}
	return res;
}

/**
 * @brief	数据加密
 * @param	src_buff  	: 数据源
 * @param	dst_buff 	: 输出流
 * @param	bytes 		: 数据量( AES 分组长度 128bit== 16Bytes, 数据量必须被该长度整除)
 * @retval	0-success	非0-error code
 * @note	使用 芯片唯一 96 bit ID 实现加密算法: 对称 - AES, 非对称 - RSA/ECC, 哈希 - MD5<不可还原>
 */
int data_encode(const void *src_buff, void *dst_buff, uint32_t bytes)
{
	#ifdef UB_SECURE_AES_ENABLE
	return mbedtls_aes_crypt_cbc(&AES, MBEDTLS_AES_ENCRYPT, bytes, IV, src_buff, dst_buff);
	#else 
	memcpy(dst_buff, src_buff, bytes);
	return 0;
	#endif
}

/**
 * @brief	数据解密
 * @param	src_buff  	: 数据源
 * @param	dst_buff 	: 输出流
 * @param	bytes 		: 数据量( AES 分组长度 128bit== 16Bytes, 数据量必须被该长度整除)
 * @retval	0-success	非0-error code
 * @note	依据 数据加密规则 data_encode() 逆向进行解码
 */
int data_decode(const void *src_buff, void *dst_buff, uint32_t bytes)
{
	#ifdef UB_SECURE_AES_ENABLE
	return mbedtls_aes_crypt_cbc(&AES, MBEDTLS_AES_DECRYPT, bytes, IV, src_buff, dst_buff);
	#else 
	memcpy(dst_buff, src_buff, bytes);
	return 0;
	#endif
}

//================================================本地函数定义=======================================================//
/**
 * @brief	重启 MCU
 * @param	/
 * @retval	/
 * @note	相比于 NVIC_SystemReset() , 看门狗从硬件层面保证可靠性, 可确保寄存器的复位值均恢复默认.
 */
static void reset_mcu(void)
{
	WDT_Init(WDT, 0, 1);
	WDT_Start(WDT);
	for (;;)
	{
	}
}

/**
 * @brief	跳转至 APP 加载地址运行
 * @param	addr : APP 加载地址
 * @retval	/
 * @note	/
 */
static void jump_to_app(const uint32_t addr)
{
	//启动先跳转到系统 BootLoader，首地址是 MSP，地址 + 4 是复位中断服务程序地址
	uint32_t sp = *((volatile uint32_t *)(addr));
	uint32_t pc = *((volatile uint32_t *)(addr + 4));

	typedef void (*Func_void_void)(void);
	Func_void_void ResetHandler = (Func_void_void)(0x00);

	/*	通过判断栈定地址值是否在 0x2000 0000 - 0x2001 0000 之间 ( SWM341 RAM Max : 0x1 0000 <64KB> ) 来判断应用程序是否已经下载到指定地址.
	 *	因为中断向量表里第一个放的就是栈顶地址的值, 如果该值不在 RAM 区间地址内, 即使这是固件也是不合法的.
	 */
	if ((sp & 0x2FFE0000) == 0x20000000 && (sp & 0x0001FFFF) < 0x00010000) // Top of Stack : __initial_sp or __user_initial_stackheap
	{
		ResetHandler = (Func_void_void)pc;

		SCB->VTOR = addr; //设置中断向量表地址

		__set_MSP(sp); // 设置主堆栈指针

		//__set_CONTROL(0);// 注意使用 RTOS 时，设置为特权级模式，使用 MSP 指针, 而不是 PSP 指针
	}
	else //该地址上没有固件, 请用户确认地址传参是否弄错了
	{
		//printf("No firmware for APP load address 0x[%x] \r\n", addr);
	}
	
	ResetHandler();

	for (;;)
	{
		// Add Your Code
	}
}

/**
 * @brief	计算 CRC 校验和
 * @param	data : APP 固件信息
 * @retval	crc  : 校验和
 * @note	/
 */
static uint32_t calculate_crc(fw_info_t *data)
{
	//重算一遍
	CRC_SetInitVal(CRC, 0);//注意初值设定需要与用户外部计算器设置保持一致 0xFFFFFFFF | 0

	CRC_Write((data->version) + (data->size));

	return CRC_Result();
}

/**
 * @brief	写入固件信息, 并自带回读校验
 * @param	ptr : 设备信息结构体指针
 * @retval	0-success	非0-error code
 * @note	/
 */
static uint8_t set_device_info(const dev_info_t *ptr)
{
	uint8_t res = 0;
	res = FLASH_Erase((uint32_t)Device_Info);
	if (res == FLASH_RES_OK)
	{
		res = FLASH_Write((uint32_t)Device_Info, (uint32_t *)ptr, sizeof(dev_info_t) >> 2);
		if (res == FLASH_RES_OK)
		{
			//回读校验
			if (Device_Info->app.version != ptr->app.version ||
				Device_Info->app.size != ptr->app.size ||
				Device_Info->app.crc != ptr->app.crc ||
				Device_Info->boot != ptr->boot ||
				Device_Info->boot_version != ptr->boot_version
				/* 												||
				Device_Info->app.reserve != ptr->app.reserve 	||
				Device_Info->reserve[0] != ptr->reserve[0]		||
				Device_Info->reserve[1] != ptr->reserve[1]
				没用的信息就懒得校验了 */
			)
			{
				//#define FLASH_RES_OK	0
				//#define FLASH_RES_TO	1	//Timeout
				//#define FLASH_RES_ERR	2
				res = 3;
			}
		}
	}
	/*
	if (res != 0)
	{
		while (1) __NOP();//不允许出现 Chip-Flash 写入失败的情况, 若出现了请排查用户层传入参数错误(数据源/地址/数据量对齐)
	}*/
	return res;
}

/**
 * @brief	固件备份 (Chip-Flash<运行区> => 本地存储<备份区>)
 * @param	path 		: 待备份固件的绝对路径
 * @param	data_enc 	: 数据加密回调
 * @retval	0-success	非0-error code
 * @note	须保证对应磁盘的 FAT 文件系统处于挂载状态
 */
static uint8_t firmware_backup(const char *path, data_code_cb data_enc)
{
	FIL file;							  //文件指针
	uint8_t res = 0;					  //返回值
	uint32_t read_len = 0, write_len = 0; //读/写文件字节数
	uint32_t offset = 0;				  // Chip-Flash 读的偏移量
	uint32_t firmware_size = 0;			  //固件大小 / Bytes

	//===============================================校验固件===============================================//
	//获取固件大小
	firmware_size = Device_Info->app.size;
	//获取固件信息
	fw_info_t firmware_info = Device_Info->app;
	printf("The [firmware_backup] Version = 0x[%x], Size = 0x[%x], CRC = 0x[%x]\r\n", firmware_info.version, firmware_info.size, firmware_info.crc);
	//判断固件是否有效 / 固件信息是否准确
	if (
		( ( *(volatile uint32_t *)(Firmware_Addr) ) & 0x2FFE0000 ) != 0x20000000 || 
		( ( *(volatile uint32_t *)(Firmware_Addr) ) & 0x0001FFFF ) >= 0x00010000 ||
		firmware_size == 0 ||
		firmware_size > Firmware_Max_Size ||
		calculate_crc(&firmware_info) != firmware_info.crc)
	{
		res = 1; 
		printf("The Firmware is invalid! calculate_crc = 0x[%x]\r\n", calculate_crc(&firmware_info));
		return res;
	}
	//===============================================校验通过===============================================//
	//	FA_CREATE_ALWAYS : 创建一个新文件，如果文件已存在，则它将被截断并覆盖
	//	FA_OPEN_ALWAYS	 : 如果文件存在，则打开；否则，创建一个新文件
	//	FA_OPEN_EXISTING : 打开一个文件。如果文件不存在，该函数将失败
	res = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS);
	if (FR_OK != res)
	{
		printf("The Firmware is [%s] file [f_open] = [%d]\r\n", path, res);
		return res;
	}
	f_lseek(&file, 0);
	//===============================================固件备份===============================================//
	//先写入 固件信息头 再写 实际固件
	res = f_write(&file, (void *)&firmware_info, sizeof(firmware_info), &write_len);
	if (FR_OK != res || write_len < sizeof(firmware_info)) //写入不完整-大概率是目录已满
	{
		printf("[%s] file [write_len] = [%d], result = [%s]\r\n", path, write_len, Debug_FR_Table[res]);
		res = FR_DENIED;
		goto firmware_backup_over;
	}
	#ifdef UB_SECURE_AES_ENABLE
	memcpy(IV, IV_Init, sizeof(IV)); //重置密文分组
	#endif
	read_len = sizeof(Write_Buff); //按 4K 读写
	for (uint32_t offset = 0; FR_OK == res && offset < firmware_size; offset += read_len)
	{
		//最后一次 Write_Buff 不满
		if (firmware_size - offset < read_len)
		{
			read_len = firmware_size - offset;
			if ((read_len & 0xF) != 0) //最后不足 16 Bytes 的部分进行 padding
			{
				read_len = (read_len & (~0xF)) + 16;
			}
		}

		if (data_enc != NULL) //加密
		{
			if (data_enc((uint8_t *)(Firmware_Addr + offset), Write_Buff, read_len) != 0)
			{
				res = 2;
				printf("The Firmware [data_enc] result = [%d]!\r\n", res);
				break;
			}
		}
		else 
		{
			memcpy(Write_Buff, (uint8_t *)(Firmware_Addr + offset), read_len);
		}

		res = f_write(&file, Write_Buff, read_len, &write_len);
		if (write_len < read_len) //写入不完整-大概率是目录已满
		{
			printf("[%s] file [write_len] = [%d], result = [%s]\r\n", path, write_len, Debug_FR_Table[res]);
			res = FR_DENIED;
			break;
		}
		printf("=============== [Firmware] Backup [%d]/[%d] Bytes ===============\r\n", offset + read_len, firmware_size);
	}
	//===============================================结束备份===============================================//
firmware_backup_over:
	f_close(&file);
	return res;
}

/**
 * @brief	固件更新 (本地存储<备份区> => Chip-Flash<运行区>)
 * @param	path 		: 待更新固件的绝对路径
 * @param	data_dec 	: 数据解密回调
 * @retval	0-success	非0-error code
 * @note	须保证对应磁盘的 FAT 文件系统处于挂载状态
 */
static uint8_t firmware_update(const char *path, data_code_cb data_dec)
{
	FIL file;					//文件指针
	uint8_t res = 0;			//返回值
	uint32_t read_len = 0;		//读文件字节数
	uint32_t offset = 0;		// Chip-Flash 擦/写的偏移量
	uint32_t firmware_size = 0; //固件大小 / Bytes

	//	FA_CREATE_ALWAYS : 创建一个新文件，如果文件已存在，则它将被截断并覆盖
	//	FA_OPEN_ALWAYS	 : 如果文件存在，则打开；否则，创建一个新文件
	//	FA_OPEN_EXISTING : 打开一个文件。如果文件不存在，该函数将失败
	res = f_open(&file, path, FA_READ | FA_OPEN_EXISTING);
	if (FR_OK != res)
	{
		printf("The Firmware is [%s] file [f_open] = [%d]\r\n", path, res);
		return res;
	}
	//===============================================校验固件===============================================//
	//文件光标定位
	f_lseek(&file, 0);
	//获取固件大小
	firmware_size = f_size(&file);
	//获取固件信息
	fw_info_t firmware_info;
	res = f_read(&file, &firmware_info, sizeof(fw_info_t), &read_len);
	if (res != FR_OK || read_len < sizeof(fw_info_t))
	{
		if (read_len < sizeof(fw_info_t))
		{
			res = 1;
		}
		printf("The Firmware don't get info, result = [%d]!\r\n", res);
		goto firmware_update_over;
	}
	firmware_size -= read_len; //得出实际的固件大小(经过 16 字节对齐填充后有可能与 firmware_info.size 不一致)
	printf("The [firmware_update] Version = 0x[%x], Size = 0x[%x], CRC = 0x[%x]\r\n", firmware_info.version, firmware_info.size, firmware_info.crc);
	
	//判断固件是否有效 / 固件信息是否准确
	if ((firmware_size & 0xF) != 0 ||
		firmware_size - firmware_info.size >= 16 ||
		firmware_size > Firmware_Max_Size ||
		calculate_crc(&firmware_info) != firmware_info.crc)
	{
		res = 2;
		printf("The Firmware is invalid! calculate_crc = 0x[%x]\r\n", calculate_crc(&firmware_info));
		goto firmware_update_over;
	}
	//===============================================校验通过===============================================//
	#ifdef UB_SECURE_AES_ENABLE
	memcpy(IV, IV_Init, sizeof(IV)); //重置密文分组
	#endif
	//===============================================更新固件===============================================//
	//进行 APP 执行区域的扇区擦除
	for (offset = 0; offset < firmware_size; offset += Flash_Alignment)
	{
		res = FLASH_Erase(Firmware_Addr + offset);
		if (res != FLASH_RES_OK)
		{
			printf("The Firmware [FLASH_Erase] is [%d]!\r\n", res);
			goto firmware_update_over;
		}
	}
	//读出 APP 新固件并写入 APP 执行区域<Chip-Flash>
	for (offset = 0; offset < firmware_size; offset += read_len)
	{
		res = f_read(&file, Read_Buff, sizeof(Read_Buff), &read_len);
		if (FR_OK != res || read_len == 0) //读取文件失败
		{
			printf("The Firmware [f_read] result = [%d]!\r\n", res);
			goto firmware_update_over;
		}

		if (data_dec != NULL) //解密
		{
			if (data_dec(Read_Buff, Write_Buff, read_len) != 0)
			{
				res = 3;
				printf("The Firmware [data_dec] result = [%d]!\r\n", res);
				goto firmware_update_over;
			}
		}
		else 
		{
			memcpy(Write_Buff, Read_Buff, read_len);
		}
		
		res = FLASH_Write(Firmware_Addr + offset, (uint32_t *)Write_Buff, read_len >> 2);
		if (res != FLASH_RES_OK)
		{
			printf("The Firmware [FLASH_Write] is [%d]!\r\n", res);
			goto firmware_update_over;
		}

		printf("=============== [Firmware] Update [%d]/[%d] Bytes ===============\r\n", offset + read_len, firmware_size);
	}
	//只有完成固件写入才会将信息头写入 Chip-Flash
	dev_info_t dev_info;
	dev_info.boot = Device_Info->boot;
	dev_info.boot_version = Device_Info->boot_version;
	dev_info.app = firmware_info;//只改变了 app 固件头, 其他都是回读重写
	dev_info.reserve[0] = Device_Info->reserve[0];
	dev_info.reserve[1] = Device_Info->reserve[1];
	//写入设备配置信息
	res = set_device_info(&dev_info);
	if (res != 0)
	{
		printf("The [set_device_info] result is [%d]!\r\n", res);
		goto firmware_update_over;
	}
	//===============================================更新结束===============================================//
firmware_update_over:
	f_close(&file);
	if (res == 0) //固件更新成功, 则将本地存储中的待更新固件删掉, 避免后续重复更新
	{
		/*res = */ f_unlink(path);
	}
	return res;
}
